package fr.thomascoffin.mocaf;

import java.awt.*;

/**
 * This class intends to ease the use of the infamous but inevitable <code>GridBagConstraints</code> class from Swing,
 * by extending it and adding convenience methods improving code conciseness and readability.
 * Please refer to the <code>java.awt.GridBagConstraints</code> for details about the meaning of its numerous attributes.
 * <p/>
 * This class provides a simple way to modify any combination of arguments in a single line, in a readable, concise way,
 * such as in the following example :<br>
 * <code>
 * JPanel panel = new JPanel(new GridBagLayout()); <br>
 * EasyGBC gbc = new EasyGBC(getGuiDefaultConstraints()); <br>
 * panel.add(_textArea, gbc.fillBoth().width(2)); <br>
 * EasyGBC buttonGBC = gbc.noFill().width(1).insets(2).goDown().weightY(0); <br>
 * panel.add(cancelButton, buttonGBC.east()); <br>
 * panel.add(clearButton, buttonGBC.goRight().center()); <br>
 * panel.add(okButton, buttonGBC.goRight().west()); <br>
 * </code>
 * <p/>
 * All methods in EasyGBC return <code>this</code>, not a copy of this. This has no impact in a series of
 * Container.add(Component, EasyGBC) calls, since the EasyGBC (as a GridBagConstraints) is cloned automatically
 * in the add() method.
 * Thus, you can use the same EasyGBC instance in successive add() calls.
 * Remember, however, not to modify a default EasyGBC instance stored in one place in your application (in order to
 * be used in several of its component), without cloning it first.
 * <p/>
 * Methods to assign special constant values such as <code>REMAINDER</code> have all been added so that the usage of
 * these values is clearer, so that with EasyGBC you never have to search through GridBagConstraints constants to see
 * if there is a special value that might be used for a field: just look at the methods.
 *
 * @see EasyGBCSimpleExample for the full code of this example.
 * @see EasyGBCFullExample for a more complete example of EasyGBC usage and as an illustration of GridBagConstraints
 *      functionalities.
 * @see EasyGBCRelativeWidthAndHeightExample to see how RELATIVE width and height work.
 * @see EasyGBCRelativeXYExample to see how RELATIVE X and Y work.
 * @see EasyGBCRemainderWidthAndHeightExample, to see how REMAINDER width and height work.
 *      <p/>
 *      This class is released by Thomas Coffin (thomas.coffin@gmail.com) under the <a href="http://www.gnu.org/copyleft/lesser.html" target="_blank">LGPL License</a>
 *      as a component of the <a href="http://code.google.com/p/mocaf" target="_blank">mocaf project</a>
 *      <p/>
 *      (c) Thomas Coffin 2008.
 */
public class EasyGBC extends GridBagConstraints
{
	/**
	 * Creates an <code>EasyGBC</code> object with all of its fields set to the passed-in arguments.
	 * The signature is the same as the full <code>GridBagConstraints</code> constructor, so that plain GridBagConstraints
	 * may be replaced easily by EasyGBC usage in existing code.
	 *
	 * @param gridx			The initial gridx value.
	 * @param gridy			The initial gridy value.
	 * @param gridwidth	The initial gridwidth value.
	 * @param gridheight The initial gridheight value.
	 * @param weightx		The initial weightx value.
	 * @param weighty		The initial weighty value.
	 * @param anchor		 The initial anchor value.
	 * @param fill			 The initial fill value.
	 * @param insets		 The initial insets value.
	 * @param ipadx			The initial ipadx value.
	 * @param ipady			The initial ipady value.
	 */
	public EasyGBC(int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, int anchor, int fill, Insets insets, int ipadx, int ipady)
	{
		super(gridx, gridy, gridwidth, gridheight, weightx, weighty, anchor, fill, insets, ipadx, ipady);
	}

	/**
	 * Constructs a default EasyGBC, with the same values as the default GridBagConstraints constructor, so that
	 * plain GridBagConstraints may be replaced easily by EasyGBC usage in existing code.
	 * Beware that the default GridBagConstraints constructor sets gridx and gridy not to 0 but to <code>RELATIVE</code> :
	 * you might want to call .x0y0() on this instance before using it.
	 *
	 * @see #RELATIVE
	 */
	public EasyGBC()
	{
		super();
	}

	/**
	 * Constructs an EasyGBC from an existing GridBagConstraints instance. Beware that the Insets are not cloned in this call :
	 * the Insets instance is shared with the original GridBagConstraints instance.
	 *
	 * @param gbc the gridbagconstraints instance.
	 */
	public EasyGBC(GridBagConstraints gbc)
	{
		this(gbc.gridx, gbc.gridy, gbc.gridwidth, gbc.gridheight, gbc.weightx, gbc.weighty, gbc.anchor, gbc.fill, gbc.insets, gbc.ipadx, gbc.ipady);
	}

	/**
	 * Updates the <code>gridx</code> and <code>gridy</code> fields both to 0 : this method will typically be called
	 * immediately after creating a new EasyGBC instance, since as in GridBagConstraints the initial x and y values are
	 * <code>RELATIVE</code> and not 0.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #RELATIVE
	 */
	public EasyGBC x0y0()
	{
		return x(0).y(0);
	}

	/**
	 * Updates the <code>gridx</code> field.
	 *
	 * @param x the new horizontal position in the grid.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC x(int x)
	{
		gridx = x;
		return this;
	}

	/**
	 * Makes one step left, decreasing the <code>gridx</code> field.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goLeft()
	{
		gridx--;
		return this;
	}

	/**
	 * Makes one step right, increasing the <code>gridx</code> field.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goRight()
	{
		gridx++;
		return this;
	}

	/**
	 * Makes n steps left, decreasing the <code>gridx</code> field.
	 *
	 * @param steps the number of steps to make on the left. May be negative.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goLeft(int steps)
	{
		gridx -= steps;
		return this;
	}

	/**
	 * Makes n steps right, increasing the <code>gridx</code> field.
	 *
	 * @param steps the number of steps to make on the right. May be negative.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goRight(int steps)
	{
		gridx += steps;
		return this;
	}

	/**
	 * Sets the <code>gridx</code> field to <code>RELATIVE</code>, specifying that the component is to be placed next
	 * to the last component added on its row : beware that the order in which the components is important when
	 * using this.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #RELATIVE for the official definition.
	 * @see EasyGBCRelativeXYExample to see this working.
	 */
	public EasyGBC relativeX()
	{
		return x(RELATIVE);
	}

	/**
	 * Sets the <code>gridy</code> field to <code>RELATIVE</code>, specifying that the component is to be placed immediately
	 * after the last component placed on its column : beware that the order in which the components is important when
	 * using this.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #RELATIVE for the official definition.
	 * @see EasyGBCRelativeXYExample to see this working.
	 */
	public EasyGBC relativeY()
	{
		return y(RELATIVE);
	}

	/**
	 * Updates the <code>gridy</code> field.
	 *
	 * @param y the new vertical position in the grid.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC y(int y)
	{
		gridy = y;
		return this;
	}

	/**
	 * Makes one step down, increasing the <code>gridy</code> field.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goDown()
	{
		gridy++;
		return this;
	}

	/**
	 * Makes one step up, decreasing the <code>gridy</code> field.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goUp()
	{
		gridy--;
		return this;
	}

	/**
	 * Makes n steps down, increasing the <code>gridy</code> field.
	 *
	 * @param steps the number of steps to make down. May be negative.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goDown(int steps)
	{
		gridy += steps;
		return this;
	}

	/**
	 * Makes n steps up, decreasing the <code>gridy</code> field.
	 *
	 * @param steps the number of steps to make up. May be negative.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC goUp(int steps)
	{
		gridy -= steps;
		return this;
	}

	/**
	 * Updates the <code>gridwidth</code> field.
	 *
	 * @param width the new width in the grid.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC width(int width)
	{
		gridwidth = width;
		return this;
	}

	/**
	 * Updates the <code>gridheight</code> field.
	 *
	 * @param height the new height in the grid.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC height(int height)
	{
		gridheight = height;
		return this;
	}

	/**
	 * Updates the <code>gridheight</code> field so that the component's display area will be from <code>gridy</code>
	 * to the last cell (included) in the column, using the <code>REMAINDER</code> special value.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #REMAINDER for the official definition.
	 * @see EasyGBCRemainderWidthAndHeightExample to see this working.
	 */
	public EasyGBC remainderHeight()
	{
		return height(REMAINDER);
	}

	/**
	 * Updates the <code>gridwidth</code> field so that the component's display area will be from <code>gridx</code>
	 * to the last cell (included) in the row, using the <code>REMAINDER</code> special value.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #REMAINDER for the official definition.
	 * @see EasyGBCRemainderWidthAndHeightExample to see this working.
	 */
	public EasyGBC remainderWidth()
	{
		return width(REMAINDER);
	}

	/**
	 * Updates the <code>gridwidth</code> field so that the component's display area will be from <code>gridx</code>
	 * to the column before the last defined in the grid, using the <code>RELATIVE</code> special value. Unlike relative x and y,
	 * the order in which the component are added is not taken into account.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #RELATIVE for the official definition of RELATIVE.
	 * @see EasyGBCRelativeWidthAndHeightExample to see it working.
	 */
	public EasyGBC relativeWidth()
	{
		return width(RELATIVE);
	}

	/**
	 * Updates the <code>gridheight</code> field so that the component's display area will be from <code>gridy</code>
	 * to the row before the last defined in the grid, using the <code>RELATIVE</code> special value. Unlike relative x and y,
	 * the order in which the component are added is not taken into account.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 * @see #RELATIVE for the official definition of RELATIVE.
	 * @see EasyGBCRelativeWidthAndHeightExample to see it working.
	 */
	public EasyGBC relativeHeight()
	{
		return height(RELATIVE);
	}

	/**
	 * Updates the <code>gridwidth</code> and <code>gridheight</code> so that the constraints take a square
	 * of the given size (in number of grid rows and columns used, not in pixels).
	 *
	 * @param squareSize the square size.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC size(int squareSize)
	{
		return width(squareSize).height(squareSize);
	}

	/**
	 * Updates simultaneously <code>gridwidth</code> and <code>gridheight</code> .
	 *
	 * @param width	the new grid width.
	 * @param height the new grid height.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC size(int width, int height)
	{
		return width(width).height(height);
	}

	/**
	 * Updates the <code>weightx</code> field.
	 *
	 * @param weightX the new horizontal weight value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC weightX(double weightX)
	{
		weightx = weightX;
		return this;
	}

	/**
	 * Updates both <code>weightx</code> and <code>weighty</code> fields with the same value.
	 *
	 * @param weight the new weight value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC weight(double weight)
	{
		weightx = weight;
		weighty = weight;
		return this;
	}

	/**
	 * Sets both <code>weightx</code> and <code>weighty</code> to <code>1.0</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC normalWeight()
	{
		return weightX(1.0).weightY(1.0);
	}

	/**
	 * Updates the <code>weighty</code> field.
	 *
	 * @param weightY the new vertical weight value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC weightY(double weightY)
	{
		weighty = weightY;
		return this;
	}

	/**
	 * Sets the anchor of the constraint.
	 *
	 * @param anchor the new anchor.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC anchor(int anchor)
	{
		this.anchor = anchor;
		return this;
	}

	/**
	 * Sets the anchor to <code>NORTH</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC north()
	{
		return anchor(NORTH);
	}

	/**
	 * Sets the anchor to <code>SOUTH</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC south()
	{
		return anchor(SOUTH);
	}

	/**
	 * Sets the anchor to <code>WEST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC west()
	{
		return anchor(WEST);
	}

	/**
	 * Sets the anchor to <code>EAST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC east()
	{
		return anchor(EAST);
	}

	/**
	 * Sets the anchor to <code>CENTER</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC center()
	{
		return anchor(CENTER);
	}

	/**
	 * Sets the anchor to <code>NORTHEAST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC northEast()
	{
		return anchor(NORTHEAST);
	}

	/**
	 * Sets the anchor to <code>NORTHWEST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC northWest()
	{
		return anchor(NORTHWEST);
	}

	/**
	 * Sets the anchor to <code>SOUTHEAST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC southEast()
	{
		return anchor(SOUTHEAST);
	}

	/**
	 * Sets the anchor to <code>SOUTHWEST</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC southWest()
	{
		return anchor(SOUTHWEST);
	}

	/**
	 * Updates the anchor to place the component centered along the edge of its display area
	 * associated with the start of a page for the current
	 * <code>ComponentOrienation</code>.  Equal to NORTH for horizontal
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC pageStart()
	{
		return anchor(PAGE_START);
	}

	/**
	 * Updates the anchor to place the component centered along the edge of its display area
	 * associated with the end of a page for the current
	 * <code>ComponentOrienation</code>.  Equal to SOUTH for horizontal
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC pageEnd()
	{
		return anchor(PAGE_END);
	}

	/**
	 * Updates the anchor to place the component centered along the edge of its display area where
	 * lines of text would normally begin for the current
	 * <code>ComponentOrienation</code>.  Equal to WEST for horizontal,
	 * left-to-right orientations and EAST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC lineStart()
	{
		return anchor(LINE_START);
	}

	/**
	 * Updates the anchor to place the component centered along the edge of its display area where
	 * lines of text would normally end for the current
	 * <code>ComponentOrienation</code>.  Equal to EAST for horizontal,
	 * left-to-right orientations and WEST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC LineEnd()
	{
		return anchor(LINE_END);
	}

	/**
	 * Updates the anchor to place the component in the corner of its display area where
	 * the first line of text on a page would normally begin for the current
	 * <code>ComponentOrienation</code>.  Equal to NORTHWEST for horizontal,
	 * left-to-right orientations and NORTHEAST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC firstLineStart()
	{
		return anchor(FIRST_LINE_START);
	}

	/**
	 * Updates the anchor to place the component in the corner of its display area where
	 * the first line of text on a page would normally end for the current
	 * <code>ComponentOrienation</code>.  Equal to NORTHEAST for horizontal,
	 * left-to-right orientations and NORTHWEST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC firstLineEnd()
	{
		return anchor(FIRST_LINE_END);
	}

	/**
	 * Updates the anchor to place the component in the corner of its display area where
	 * the last line of text on a page would normally start for the current
	 * <code>ComponentOrienation</code>.  Equal to SOUTHWEST for horizontal,
	 * left-to-right orientations and SOUTHEAST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC lastLineStart()
	{
		return anchor(LAST_LINE_START);
	}

	/**
	 * Updates the anchor to place the component in the corner of its display area where
	 * the last line of text on a page would normally end for the current
	 * <code>ComponentOrienation</code>.  Equal to SOUTHEAST for horizontal,
	 * left-to-right orientations and SOUTHWEST for horizontal, right-to-left
	 * orientations.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC lastLineEnd()
	{
		return anchor(LAST_LINE_END);
	}

	/**
	 * Sets the <code>fill</code> field.
	 *
	 * @param fill the new fill value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC fill(int fill)
	{
		this.fill = fill;
		return this;
	}

	/**
	 * Sets the <code>fill</code> field to <code>NONE</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC noFill()
	{
		return fill(NONE);
	}

	/**
	 * Sets the <code>fill</code> field to <code>HORIZONTAL</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC horizontalFill()
	{
		return fill(HORIZONTAL);
	}

	/**
	 * Sets the <code>fill</code> field to <code>VERTICAL</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC verticalFill()
	{
		return fill(VERTICAL);
	}

	/**
	 * Sets the <code>fill</code> field to <code>BOTH</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC fillBoth()
	{
		return fill(BOTH);
	}

	/**
	 * Sets the <code>insets</code> field.
	 *
	 * @param insets the insets to use.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC insets(Insets insets)
	{
		this.insets = insets;
		return this;
	}

	/**
	 * Modifies the current insets using the given value at top, bottom, left and right. Beware that this does not
	 * clone the insets instance previously.
	 *
	 * @param inset the value to use for insets in all directions.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC insets(int inset)
	{
		insets.set(inset, inset, inset, inset);
		return this;
	}

	/**
	 * Sets the <code>padx</code> field.
	 *
	 * @param padX the new padx value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC padX(int padX)
	{
		this.ipadx = padX;
		return this;
	}

	/**
	 * Sets the <code>pady</code> field.
	 *
	 * @param padY the new pady value.
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC padY(int padY)
	{
		this.ipady = padY;
		return this;
	}

	/**
	 * Sets both <code>padx</code> and <code>pady</code> to <code>0</code>.
	 *
	 * @return the modified EasyGBC instance : NOT a modified copy of this, as for all methods in EasyGBC.
	 */
	public EasyGBC noPad()
	{
		return padX(0).padY(0);
	}

	public EasyGBC clone()
	{
		return (EasyGBC) super.clone();
	}

	/**
	 * Provides a full description of the user attributes.
	 *
	 * @return a full description of the user attributes.
	 */
	public String toString()
	{
		return this.getClass().getName() + " (gridx = " + gridx + ", gridy = " + gridy + ", gridwidth = " + gridwidth + ", gridheight = " + gridheight + ", weightx = " + weightx + ", weighty = " + weighty + ", anchor = " + anchor + ", fill = " + fill + ", insets = " + insets + ", ipadx = " + ipadx + ", ipady = " + ipady;
	}
}
